package com.study.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.study.cacheManager.GroupCacheManager;
import com.study.constant.RedissonLockConstant;
import com.study.convention.errorCode.BaseErrorCode;
import com.study.convention.exception.ClientException;
import com.study.convention.exception.RemoteException;
import com.study.convention.result.Result;
import com.study.dao.entity.TGroup;
import com.study.dao.mapper.TGroupMapper;
import com.study.dto.req.ShortLinkGroupSaveReqDTO;
import com.study.dto.req.ShortLinkGroupSortReqDTO;
import com.study.dto.req.ShortLinkGroupUpdateReqDTO;
import com.study.dto.resp.ShortLinkGroupCountQueryRespDTO;
import com.study.dto.resp.ShortLinkGroupRespDTO;
import com.study.feignClient.GroupShortLinkClient;
import com.study.service.GroupService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.study.common.UserContext.getUsername;

/**
 * @author canace
 * @version 1.0
 * @description 短链接分组服务实现类
 * @date 2024/1/23 19:45
 */

@Service
@RequiredArgsConstructor
@Slf4j
public class GroupServiceImpl extends ServiceImpl<TGroupMapper, TGroup> implements GroupService {

    private final RedissonClient redissonClient;

    private final GroupCacheManager groupCacheManager;

    private final GroupShortLinkClient groupShortLinkClient;


    @Value("${short-link.group.max-num}")
    private Integer groupMaxNum;

    @Override
    public void addGroup(ShortLinkGroupSaveReqDTO shortLinkGroupSaveReqDTO) {
        // 获取当前登录用户名
        String username = getUsername();

        // 首先检查当前用户创建的分组数量是否超过限制
        LambdaQueryWrapper<TGroup> eq = Wrappers.lambdaQuery(TGroup.class)
                .eq(TGroup::getUsername, username)
                .eq(TGroup::getDelFlag, 0);
        Long count = baseMapper.selectCount(eq);
        if (count >= groupMaxNum) {
            throw new ClientException(BaseErrorCode.GROUP_NAME_IS_MAX_ERROR);
        }

        // 获取分布式锁
        RLock lock = redissonClient.getLock(RedissonLockConstant.LOCK_GROUP_CREATE_KEY + username);

        // 尝试获取锁
        try {
            if (lock.tryLock()) {
                // 创建分组
                TGroup tGroup = TGroup.builder()
                        .name(shortLinkGroupSaveReqDTO.getName())
                        .sortOrder(0)
                        .username(username)
                        // 使用雪花算法生成分组标识
                        .gid(IdUtil.getSnowflakeNextIdStr())
                        .build();

                int insert = baseMapper.insert(tGroup);
                if (insert < 1) {
                    throw new ClientException(BaseErrorCode.GROUP_CREATE_ERROR);
                }

            }
        } finally {
            lock.unlock();
        }

    }

    @Override
    public List<ShortLinkGroupRespDTO> getShortLinkGroupList() {
        // 获取当前登录用户
        String username = getUsername();

        // 根据用户名查询短链接分组
//        LambdaQueryWrapper<TGroup> tGroupLambdaQueryWrapper = Wrappers.lambdaQuery(TGroup.class)
//                .eq(TGroup::getDelFlag, 0)
//                .eq(TGroup::getUsername, username)
//                .orderByDesc(TGroup::getSortOrder)
//                .orderByDesc(TGroup::getUpdateTime);
//        List<TGroup> tGroups = baseMapper.selectList(tGroupLambdaQueryWrapper);
//        List<TGroup> tGroups = groupCacheManager.getGroupListByUsername(username);
        List<TGroup> tGroups = groupCacheManager.getGroupListByUsername(username);
        if (tGroups.isEmpty()) {
            throw new ClientException(BaseErrorCode.GROUP_NO_SHORT_LINK);
        }
//        return BeanUtil.copyToList(tGroups, ShortLinkGroupRespDTO.class);
        // 使用feign调用project微服务，获取当前用户短链接分组中有多少个短链接
        Result<List<ShortLinkGroupCountQueryRespDTO>> listResult = groupShortLinkClient.listGroupShortLinkCount(tGroups.stream().map(TGroup::getGid).toList());
        // 如果返回值为空或者code不为0
        if (ObjectUtil.isEmpty(listResult) || !"0".equals(listResult.getCode())) {
            throw new RemoteException(BaseErrorCode.REMOTE_ERROR);
        }
        // 将上面的集合转为Map，便于后续查找对应值
        Map<String, Integer> map = listResult.getData().stream().collect(Collectors.toMap(ShortLinkGroupCountQueryRespDTO::getGid, ShortLinkGroupCountQueryRespDTO::getShortLinkCount));
        //结果封装返回
        return BeanUtil.copyToList(tGroups, ShortLinkGroupRespDTO.class).stream().peek(t -> t.setShortLinkCount(map.getOrDefault(t.getGid(), 0))).toList();
    }

    @Override
    public void updateShortLinkGroup(ShortLinkGroupUpdateReqDTO shortLinkGroupUpdateReqDTO) {
        // 根据gid获取分组详情
        TGroup tGroup = gettGroup(shortLinkGroupUpdateReqDTO.getGid());

        // 获取分布式锁进行更新操作 这里使用用户名作为分布式锁的key，因为一个用户只能对这个表做一个更新/删除操作
        RLock lock = redissonClient.getLock(RedissonLockConstant.LOCK_GROUP_UPDATE_KEY + tGroup.getUsername());

        try {
            if (lock.tryLock()) {
                // 更新分组信息
                tGroup.setName(shortLinkGroupUpdateReqDTO.getName());
                int update = baseMapper.updateById(tGroup);
                if (update < 1) {
                    throw new ClientException(BaseErrorCode.GROUP_UPDATE_ERROR);
                }
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void deleteShortLinkGroup(String gid) {

        // 虽然是删除操作，但是本质上还是更新，将delFlag更新为1
        TGroup tGroup = gettGroup(gid);

        // 获取分布式锁进行更新操作
        RLock lock = redissonClient.getLock(RedissonLockConstant.LOCK_GROUP_UPDATE_KEY + tGroup.getUsername());

        try {
            if (lock.tryLock()) {
                // 更新分组信息
                tGroup.setDelFlag(1);
                int update = baseMapper.updateById(tGroup);
                if (update < 1) {
                    throw new ClientException(BaseErrorCode.GROUP_UPDATE_ERROR);
                }
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void sortShortLinkGroup(List<ShortLinkGroupSortReqDTO> shortLinkGroupSortReqDTO) {
        // 先从数据库中查询出当前用户所有的分组信息
        List<ShortLinkGroupRespDTO> shortLinkGroupList = getShortLinkGroupList();

        // 将这些数据和更新后的排序数据进行比较，如果不一致，则更新
        shortLinkGroupList.stream().filter(shortLinkGroup ->
                // gid相同，但是排序不一致的进行后面的更新操作，主要为了减少数据库的更新操作
                shortLinkGroupSortReqDTO.stream().anyMatch(s -> s.getGid().equals(shortLinkGroup.getGid()) && !s.getSortOrder().equals(shortLinkGroup.getSortOrder()))
        ).forEach(shortLinkGroup -> {
            // 将需要更新的进行更新排序
            shortLinkGroup.setSortOrder(shortLinkGroupSortReqDTO.stream().filter(s -> s.getGid().equals(shortLinkGroup.getGid())).findFirst().get().getSortOrder());
            TGroup build = TGroup.builder().gid(shortLinkGroup.getGid()).sortOrder(shortLinkGroup.getSortOrder()).build();

            // 获取分布式锁进行更新操作
            RLock lock = redissonClient.getLock(RedissonLockConstant.LOCK_GROUP_UPDATE_KEY + build.getUsername());
            try {
                if (lock.tryLock()) {
                    LambdaQueryWrapper<TGroup> tGroupLambdaQueryWrapper = Wrappers.lambdaQuery(TGroup.class).eq(TGroup::getGid, shortLinkGroup.getGid()).eq(TGroup::getUsername, getUsername()).eq(TGroup::getDelFlag, 0);
                    int update = baseMapper.update(build, tGroupLambdaQueryWrapper);
                    if (update < 1) {
                        throw new ClientException(BaseErrorCode.GROUP_UPDATE_ERROR);
                    }
                }
            } finally {
                lock.unlock();
            }
        });


    }

    /**
     * 根据分组gid获取分组信息
     *
     * @param gid 分组gid
     * @return 分组信息
     */
    private TGroup gettGroup(String gid) {
        // 根据gid获取分组详情
//        LambdaQueryWrapper<TGroup> tGroupLambdaQueryWrapper = Wrappers.lambdaQuery(TGroup.class)
//                .eq(TGroup::getGid, gid)
//                .eq(TGroup::getDelFlag, 0);
//        TGroup tGroup = baseMapper.selectOne(tGroupLambdaQueryWrapper);
        TGroup tGroup = groupCacheManager.getGroupByGid(gid);
        if (ObjectUtil.isEmpty(tGroup)) {
            throw new ClientException(BaseErrorCode.GROUP_NO_EXIST);
        }
        // 判断是否为当前用户的分组
        if (!getUsername().equals(tGroup.getUsername())) {
            throw new ClientException(BaseErrorCode.GROUP_NO_AUTH);
        }
        return tGroup;
    }
}
